#ifndef STX_ALGORITHM_RANGE_HPP_
#define STX_ALGORITHM_RANGE_HPP_

#include <stx/algorithm/advance.hpp>
#include <stx/algorithm/begin.hpp>
#include <stx/algorithm/end.hpp>

#include <vector>
#include <deque>
#include <list>

namespace stx {

template <class First, class Last, class Step>
class value_range
{
public:
    
    typedef First value_type;
    
    value_range(First first, Last last, Step step)
    {
        first_ = first;
        last_ = last;
        step_ = step;
    }
    
    template <class T, class Allocator>
    operator std::vector<T, Allocator> ()
    {
        return generate<std::vector<T, Allocator> >();
    }
    
    template <class T, class Allocator>
    operator std::deque<T, Allocator> ()
    {
        return generate<std::deque<T, Allocator> >();
    }
    
    template <class T, class Allocator>
    operator std::list<T, Allocator> ()
    {
        return generate<std::list<T, Allocator> >();
    }
    
private:
    
    template <class Container>
    Container generate()
    {
        // todo: Think about reserving space for elements.
        Container c;
        if ((last_ <= first_ && step_ >= 0) || (last_ >= first_ && step_ <= 0) || step_ == 0) {
            return c;
        }
        value_type x = first_;
        if (first_ < last_) {
            for (; x < last_; x += step_) {
                c.push_back(x);
            }
        } else {
            for (; x > last_; x += step_) {
                c.push_back(x);
            }
        }
        return c;
    }
    
    First first_;
    Last last_;
    Step step_;
};

template <class T1, class T2, class T3>
inline value_range<T1, T2, T3>
range_impl(T1 first, T2 last, T3 step)
{
    return value_range<T1, T2, T3>(first, last, step);
}

template <class T1, class T2>
inline value_range<T1, T2, T1>
range_impl(T1 first, T2 last)
{
    return value_range<T1, T2, T1>(first, last, (T1) 1);
}

template <class T1>
inline value_range<T1, T1, T1>
range_impl(T1 last)
{
    return value_range<T1, T1, T1>((T1) 0, last, (T1) 1);
}

inline value_range<int, int, int>
range(int first, int last, int step)
{
    return range_impl(first, last, step);
}

inline value_range<int, int, int>
range(int first, int last)
{
    return range_impl(first, last);
}

inline value_range<int, int, int>
range(int last)
{
    return range_impl(last);
}

inline value_range<float, float, float>
range(float first, float last, float step)
{
    return range_impl(first, last, step);
}

inline value_range<float, float, float>
range(float first, float last)
{
    return range_impl(first, last);
}

inline value_range<float, float, float>
range(float last)
{
    return range_impl(last);
}

inline value_range<double, double, double>
range(double first, double last, double step)
{
    return range_impl(first, last, step);
}

inline value_range<double, double, double>
range(double first, double last)
{
    return range_impl(first, last);
}

inline value_range<double, double, double>
range(double last)
{
    return range_impl(last);
}

//*/
template <class InputIterator>
class iterator_range
{
public:
    
    typedef InputIterator iterator;
    
    iterator_range(InputIterator first, InputIterator last):
        begin_(first), end_(last)
    {
    }
    
    void reset(InputIterator first, InputIterator last)
    {
        begin_ = first;
        end_ = last;
    }
    
    InputIterator begin()
    {
        return begin_;
    }
    
    InputIterator end()
    {
        return end_;
    }
    
private:
    
    InputIterator begin_;
    InputIterator end_;
};

template<class InputIterator>
inline iterator_range<InputIterator>
range(InputIterator first, InputIterator last)
{
    return iterator_range<InputIterator>(first, last);
}

template<class Container>
inline iterator_range<typename Container::iterator>
range(Container& c)
{
    return iterator_range<typename Container::iterator>(stx::begin(c), stx::end(c));
}

template<class Container>
inline iterator_range<typename Container::const_iterator>
range(const Container& c)
{
    return iterator_range<typename Container::const_iterator>(stx::begin(c), stx::end(c));
}

template<class Container>
inline iterator_range<typename Container::iterator>
range(Container& c, typename Container::iterator last)
{
    typename Container::iterator first(stx::begin(c));
    return iterator_range<typename Container::iterator>(first, last);
}

template<class Container>
inline iterator_range<typename Container::const_iterator>
range(const Container& c, typename Container::const_iterator last)
{
    typename Container::const_iterator first(stx::begin(c));
    return iterator_range<typename Container::iterator>(first, last);
}

template<class Container>
inline iterator_range<typename Container::iterator>
range(Container& c, typename Container::size_type count)
{
    typename Container::iterator first(stx::begin(c));
    typename Container::iterator last(first);
    stx::advance(last, count);
    return iterator_range<typename Container::iterator>(first, last);
}

template<class Container>
inline iterator_range<typename Container::const_iterator>
range(const Container& c, typename Container::size_type count)
{
    typename Container::const_iterator first(stx::begin(c));
    typename Container::const_iterator last(first);
    stx::advance(last, count);
    return iterator_range<typename Container::iterator>(first, last);
}

template<class Container>
inline iterator_range<typename Container::iterator>
range(Container& c, typename Container::size_type from, typename Container::size_type to)
{
    typename Container::iterator first(stx::begin(c));
    stx::advance(first, from);
    typename Container::iterator last(first);
    stx::advance(last, to - from);
    return iterator_range<typename Container::iterator>(first, last);
}

template<class Container>
inline iterator_range<typename Container::const_iterator>
range(const Container& c, typename Container::size_type from, typename Container::size_type to)
{
    typename Container::const_iterator first(stx::begin(c));
    stx::advance(first, from);
    typename Container::const_iterator last(first);
    stx::advance(last, to - from);
    return iterator_range<typename Container::iterator>(first, last);
}

} // namespace stx

#endif // STX_ALGORITHM_RANGE_HPP_
